using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;

using ExpertPdf.HtmlToPdf;

namespace Console_MultithreadedPerformance
{
    class Program
    {
        const bool MULTITHREADED = true;
        const int THREADS_COUNT = 10;
        const int CONVERSIONS_PER_THREAD_COUNT = 50;

        static string urlToConvert = null;
        static int totalConversionsCount = 0;
        static DateTime profilingStartTime = DateTime.MinValue;

        static void Main(string[] args)
        {
            if (args.Length > 2)
            {
                Console.WriteLine("Usage: Console_MultithreadedPerformance.exe [URL] ");
                return;
            }

            string appPath = GetAppPath();

            if (args.Length == 1)
                urlToConvert = args[0];
            else
                urlToConvert = Path.Combine(appPath, @"..\..\HTML\htmltopdf.htm");

            
            if (MULTITHREADED)
            {
                Console.WriteLine(String.Format("Output directory: {0}\n", Path.Combine(GetAppPath(), @"..\..\OutPDF")));
                Console.WriteLine(String.Format("Profiling started. {0} threads. {1} conversions per thread. URL: {2}.\n", 
                    THREADS_COUNT, CONVERSIONS_PER_THREAD_COUNT, urlToConvert));

                totalConversionsCount = 0;
                profilingStartTime = DateTime.Now;

                // start the worker threads to convert HTML to PDF
                Thread[] workerThreads = new Thread[THREADS_COUNT];
                for (int threadIdx = 0; threadIdx < THREADS_COUNT; threadIdx++)
                {
                    workerThreads[threadIdx] = new Thread(new ParameterizedThreadStart(ConvertHtmlToPdf));
                    workerThreads[threadIdx].Start(threadIdx);
                }

                // wait for the worker threads to end
                for (int threadIdx = 0; threadIdx < workerThreads.Length; threadIdx++)
                    workerThreads[threadIdx].Join();

                // global statistics

                DateTime profilingEndTime = DateTime.Now;

                // the total time since all conversions started
                double totalTime = profilingEndTime.Subtract(profilingStartTime).TotalSeconds;
                // the average conversion speed in PDFs per second
                double averageConversionSpeed = ((double)totalConversionsCount) / totalTime;

                Console.WriteLine(String.Format("\nProfiling ended. {0} conversions in {1:F2} sec. Avg speed: {2:F2} pdf/sec.",
                    totalConversionsCount, totalTime, averageConversionSpeed));
                Console.WriteLine("Press ENTER to exit.");

                Console.ReadLine();
            }
            else
            {
                Console.WriteLine(String.Format("Output directory: {0}\n", Path.Combine(GetAppPath(), @"..\..\OutPDF")));
                Console.WriteLine(String.Format("Profiling started. Not multithreaded. URL: {0}.\n", urlToConvert));

                totalConversionsCount = 0;
                profilingStartTime = DateTime.Now;
                double totalTime = 0;
                double averageConversionSpeed = 0;

                for (int convIdx = 0; convIdx < THREADS_COUNT * CONVERSIONS_PER_THREAD_COUNT; convIdx++)
                {
                    try
                    {
                        // create the HTML to PDF converter
                        PdfConverter pdfConverter = new PdfConverter();
                        // get the output PDF file path
                        string outPdfFile = Path.Combine(Path.Combine(GetAppPath(), @"..\..\OutPDF"), String.Format("RenderedPage_{0}.pdf", convIdx));

                        DateTime startTime = DateTime.Now;

                        // do the HTML to PDF conversion
                        pdfConverter.SavePdfFromUrlToFile(urlToConvert, outPdfFile);

                        DateTime endTime = DateTime.Now;

                        // the time in seconds taken by this conversion
                        double thisConversionTime = endTime.Subtract(startTime).TotalSeconds;

                        // the total number of conversions
                        totalConversionsCount++;
                        // the total time since all conversions started
                        totalTime = endTime.Subtract(profilingStartTime).TotalSeconds;
                        // the average conversion speed in PDFs per second
                        averageConversionSpeed = ((double)totalConversionsCount) / totalTime;

                        Console.WriteLine(String.Format("Iteration {0}: {1:F2} sec. Avg speed: {2:F2} pdf/sec", convIdx, 
                            thisConversionTime, averageConversionSpeed));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }

                // global statistics

                DateTime profilingEndTime = DateTime.Now;

                // the total time since all conversions started
                totalTime = profilingEndTime.Subtract(profilingStartTime).TotalSeconds;
                // the average conversion speed in PDFs per second
                averageConversionSpeed = ((double)totalConversionsCount) / totalTime;

                Console.WriteLine(String.Format("\nProfiling ended. {0} conversions in {1:F2} sec. Avg speed: {2:F2} pdf/sec.",
                    totalConversionsCount, totalTime, averageConversionSpeed));
                Console.WriteLine("Press ENTER to exit.");

                Console.ReadLine();
            }
        }

        private static void ConvertHtmlToPdf(object threadIdxObj)
        {
            int threadIdx = (int)threadIdxObj;


            for (int convIdx = 0; convIdx < CONVERSIONS_PER_THREAD_COUNT; convIdx++)
            {
                try
                {
                    // create the HTML to PDF converter
                    PdfConverter pdfConverter = new PdfConverter();
                    // get the output PDF file path
                    string outPdfFile = Path.Combine(Path.Combine(GetAppPath(), @"..\..\OutPDF"), String.Format("RenderedPage_{0}_{1}.pdf", threadIdx, convIdx));

                    DateTime startTime = DateTime.Now;

                    // do the HTML to PDF conversion
                    pdfConverter.SavePdfFromUrlToFile(urlToConvert, outPdfFile);

                    DateTime endTime = DateTime.Now;

                    // the time in seconds taken by this conversion
                    double thisConversionTime = endTime.Subtract(startTime).TotalSeconds;

                    // the total number of conversions
                    int totalConversions = Interlocked.Increment(ref totalConversionsCount);
                    // the total time since all conversions started
                    double totalTime = endTime.Subtract(profilingStartTime).TotalSeconds;
                    // the average conversion speed in PDFs per second
                    double averageConversionSpeed = ((double)totalConversions)/totalTime;
                    
                    Console.WriteLine(String.Format("Thread {0} iteration {1}: {2:F2} sec. Conversions: {3}. Avg speed: {4:F2} pdf/sec", threadIdx, convIdx,
                        thisConversionTime, totalConversions, averageConversionSpeed));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }

        }

        private static string GetAppPath()
        {
            return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
        }
    }
}
